package com.tridion.utilities.verifier.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import com.tridion.utilities.verifier.model.JarFile;

/**
 * Utility class used for getting version information from jar files.
 * 
 * @author Daniel Neagu
 */
public class JarUtil {
    
    /**
     * Extension for files which we're interested in.
     */
    private static final String EXTENSION = ".jar";
    
    /**
     * Different possibilities for zip entry manifest file (all the same in the end but different from the Zipping 
     * point of view).
     */
    private static final String[] MANIFEST_FILE = new String[]{"/META-INF/Manifest.mf", "/meta-inf/manifest.mf",
        "meta-inf/manifest.mf", "META-INF/MANIFEST.MF"};
    
    /**
     * Different ways of representing the version string inside the jar manifest.
     */
    private static final String[] VERSION_STRINGS = new String[]{"TCD-build-version:", "Implementation-Version:",
        "Specification-Version:", "Bundle-Version:"};
    
    private JarUtil() {} // we have no use of instances
    
    /**
     * Lists the jar versions of all jars in a certain folder.
     * 
     * @param folder The folder containing all jars.
     * @return A list with all jars and their related extracted information.
     */
    public static List<JarFile> listJars(final File folder) {
        final List<JarFile> jarsVersion = new ArrayList<JarFile>();
        
        // get all files in that folder
        final File[] jars = folder.listFiles();

        if (jars != null && jars.length > 0) {
            // let's loop through them and get the version of each jar file
            for (File file : jars) {
                if (file.getName().endsWith(EXTENSION)) {
                    final JarFile jv = new JarFile();
                    jv.setFile(file);
                    jv.setVersion(getJarVersion(file));
                    jv.setName(file.getName());
                    jv.setChecksum(ChecksumUtil.generateChecksum(file));
                    
                    jarsVersion.add(jv);
                }
            }
        } else {
            final JarFile jv = new JarFile();
            jv.setFile(new File("<Error>"));
            jv.setVersion("Given folder is empty");
            
            jarsVersion.add(jv);
        }
        
        return jarsVersion;
    }

    /**
     * Gets the version of the given source jar file.
     * 
     * @param source The jar to which we want to know the version.
     * @return The version of the parameter source jar.
     */
    static String getJarVersion(final File source) {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(source);
            ZipEntry entry = null; // TCD-build-version: 6.1.0.348
            
            // in some of the jars the manifest file has a strange name...let's make sure we cover all possibilities
            for (String manifestFile : MANIFEST_FILE) {
                // get the zip entry for the manifest file
                entry = zipFile.getEntry(manifestFile);
                if (entry != null) {
                    break;
                }
            }

            if (entry != null) {
                // let's peak into the manifest file
                final InputStream inStream = zipFile.getInputStream(entry);

                final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream,
                        Charset.forName("UTF-8")));
                String line;
                while ((line = reader.readLine()) != null) {
                    // let's see if the current line is a line containing a version string
                    for (String versionString : VERSION_STRINGS) {
                        if (line.startsWith(versionString)) {
                            return line.substring(versionString.length()).trim();
                        } 
                    }
                }
            } else {
                return "no manifest file";
            }
        } catch (ZipException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        return "unknown";
    }
}
